package liquidfs.data.fs;

import java.io.*;
import java.io.FileNotFoundException;
import java.util.*;

import utility.*;

import com.thoughtworks.xstream.annotations.*;

import liquidfs.core.*;
import liquidfs.data.*;
import liquidfs.data.ex.*;
import liquidfs.data.fs.util.*;
import liquidfs.data.ops.*;

@XStreamAlias("liquid-file")
public class LocalLiquidFile extends LocalLiquidObject implements LiquidFile {
   
   private File localFile;
   private LiquidDataCache dataCache = null;
   private LiquidMetadataCache metaCache = null;
   
   public LocalLiquidFile(LiquidFileSystem fs, String path) {
      super(fs, path);
      
      this.localFile = this.getFileSystem().getLocalFile(path);
      this.refresh();
   }
   
   public LiquidMetadataCache getMetadataCache () {
      return this.metaCache;
   }
   
   @Override
   public void refresh () {
      this.metaCache = new LiquidMetadataCache(this.getPath(), localFile.length(), this.getFileSystem().getMetadata(this.getPath()), false);
   }

   @Override
   public long getSize() {
      if (this.metaCache.getSize() == null) {
         refresh();
      }
      
      return this.metaCache.getSize();
   }
   
   @Override
   public Metadata getMetadata () {
      if (this.metaCache.getMetadata() == null) {
         refresh();
      }
      
      return this.metaCache.getMetadata();
   }

   @Override
   public Date getCreationDate() {
      return this.getMetadata().getCreationDate();
   }

   @Override
   public Date getModificationDate() {
      return this.getMetadata().getModificationDate();
   }

   @Override
   public Date getLastAccessedDate() {
      return this.getMetadata().getLastAccessedDate();
   }

   @Override
   public boolean isDirectory() {
      return false;
   }
   
   @Override
   public LiquidDirectory asDirectory () {
      throw new WrongObjectTypeException("Filepath " + this.getPath() + " refers to a file, not a directory.");
   }
   
   @Override
   public LiquidFile asFile () {
      return this;
   }
   
   public void open (OpenMode mode) {
      this.dataCache = new LiquidDataCache(this.getFileSystem(), this);
      this.dataCache.open(mode);
   }
   
   public void close () {
      this.dataCache.close();
      this.dataCache = null;
   }
   
   @Override
   public void truncate () {
      
      if (this.dataCache == null) {
         throw new FileSystemException("File " + this.getPath() + " is not open.");
      }
      
      this.dataCache.truncate ();
   }
   
   @Override
   public void setLength (long length) {
      
      if (this.dataCache == null) {
         throw new FileSystemException("File " + this.getPath() + " is not open.");
      }
      
      this.dataCache.setLength (length);
   }
   
   public boolean isOpen () {
      return (this.dataCache != null && this.dataCache.isOpen());
   }
   
   @Override 
   public InputStream getInputStream() {
      return this.getInputStream(0);
   }
   
   @Override 
   public InputStream getInputStream(long offset) {
      
      if (this.dataCache == null) {
         throw new FileSystemException("File " + this.getPath() + " is not open.");
      }
      
      return this.dataCache.getInputStream(offset);
   }
   
   @Override
   public OutputStream getOutputStream() {
      return this.getOutputStream(0);
   }
   
   @Override
   public OutputStream getOutputStream(long offset) {
      
      if (this.dataCache == null) {
         throw new FileSystemException("File " + this.getPath() + " is not open.");
      }
      
      return this.dataCache.getOutputStream(offset);
   }

   @Override
   public List<PeerDevice> getStorageDevices() {
      
      if (this.metaCache.getStorageDevices() == null) {
         QueryStorageDevicesOperation op = new QueryStorageDevicesOperation(this.getFileSystem().getLocalDevice(), this.getPath());
         Result<List<PeerDevice>> result = this.getFileSystem().process(op);
         this.metaCache.setStorageDevices(result.getData());
      }
      
      return this.metaCache.getStorageDevices();
   }
}
